ES6中有关 class 的继承方式，引入了 extends 关键字。
但其本质仍然是 构造函数 + 原型链的 组合式继承。

class A {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  getName() {
    console.log(this.name);
  }
}

class B extends A {
  constructor(name, age) {
    super(name, age);
    this.job = "web";
  }
  getJob() {
    console.log(this.job);
  }
  getNameAndJob() {
    onsole.log(super.getName() + this.job);
  }
}

var b = new B("小黄", 20);
console.log(b.name); //小黄
console.log(b.age); //20
b.getName(); //小黄
b.getJob(); //web
b.getNameAndJob(); //小黄 web
# extends
上述 B 类（class）通过 extends 关键字，继承了 A 类 的所有属性和方法。A 类中的所有方法默认是添加到 B 的原型上，所以 extends 继承的实质仍然是原型链。

console.log("constructor" in b); //true
console.log("getName" in b); //true
console.log(b.hasOwnProperty("getName")); //false
console.log(b.hasOwnProperty("constructor")); //false
# super
super 这个关键字，既可以当作函数使用，也可以当作对象使用。当作函数使用时super 代表父类的构造函数，并在子类中执行 Parent.apply(this)，从而将父类实例对象的属性和方法，添加到子类的 this上面。

特别注意

子类必须在 constructor 方法中调用 super 方法，如果子类没有定义 constructor 方法，constructor 方法以及其内部的 super 方法会被默认添加。
class A {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  getName() {
    console.log(this.name);
  }
}

class B extends A { }

var b = new B("小黄", 20);

console.log(b.name); //小黄
console.log(b.age); //20
b.getName(); //小黄
console.log(b.hasOwnProperty("name")); //true
在子类的 constructor 方法中，只有调用 super 之后，才可以使用 this 关键字，否则会报错。
class A {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

class B extends A {
  constructor(name, age) {
    this.job = "web";
    super(name, age);
  }
}

var b = new B("小黄", 20); // 报错
super() 只能用在子类的constructor方法之中，用在其他地方就会报错。
class A {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

class B extends A {
  toStr(name, age) {
    super(name, age)
  }
}

var b = new B("Tom", 20); //报错
super 作为对象时，在子类中指向父类的原型对象。即 super = Parent.prototype。

class A {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  getName() {
    console.log(this.name);
  }
}
A.prototype.n = 33;

class B extends A {
  constructor(name, age) {
    super(name, age);
  }
  toStr() {
    return super.n;
  }
  activeGetName() {
    super.getName();
  }
}

var b = new B("小黄", 20);
console.log(b.toStr());  //3
console.log(b.activeGetName()); //小黄
# 静态方法的继承
在一个方法前加上关键字 static，就表示该方法不会被实例继承，但是父类的静态方法，会被子类继承。

class A {
  static say() {
    console.log("hello");
  }
}

class B extends A { }

console.log(B.say()); //hello
也可以使用 super 在子类的静态方法中调用父类的静态方法。super 在静态方法中指向父类本身，而不是父类的原型对象。

class A {
  static say() {
    console.log("hello");
  }
}

class B extends A {
  static toStr() {
    super.say();
  }
}
var b = new B();
console.log(B.toStr()); //hello
# 继承表达式的类
类不但可以继承自其他类，也可以继承表达式。只要表达式可以被解析为一个函数并且通过 new 关键字可以创建新的实例对象即可。

继承传统形式的构造函数
let Obj = function (name) {
  this.name = name;
}
Obj.prototype.getName = function () {
  console.log(this.name);
}

class Person extends Obj {
  constructor(name, age) {
    super(name);
    this.age = age;
  }
}

const p = new Person("小黄", 29);

console.log(p.name); //小黄
console.log(p.age); //29
p.getName(); //小黄
继承函数返回结果
let fn = function () {
  return class Person {
    constructor(name) {
      return {
        name
      }
    }
  }
}

class SomeOne extends fn() {
  constructor(name) {
    super(name);
  }
}

let p = new SomeOne("小黄");
console.log(p.name); //小黄
# New.target
我们知道，函数内部有一个 new.target 对象用于判断函数是否通过new关键字调用。类构造函数也可以通过 new.target 来确定类的调用形式。


class Obj {
  //new Obj()时，new.target 的值为 Obj
  constructor() {
    if (new.target === Obj) {
      console.log("不可以直接调用基类！");
    }
  }
  fn() {
    console.log(this.name);
  }
}

class Person extends Obj {
  //new Person("小黄")时，new.target的值为 Person
  constructor(name) {
    super();
    this.name = name;
  }
}

let p1 = new Person("小黄");
p1.fn(); //小黄
let p2 = new Obj(); // 不可以直接调用基类！
因为类必须通过 new 关键字调用，所以在类的构造函数中 new.target 的值永远不会是undefined。
